package protobufrpc

import (
	"context"
	"errors"
	"github.com/golang/protobuf/proto"
	"reflect"
	"strings"
)

var m = make(map[string]reflect.Value)

var MethodNotFoundError = errors.New("method not found")

func Run(srv, fn string, ctx context.Context, req []byte) (proto.Message, interface{}, error) {

	var value, find = m[srv]
	if !find {
		return nil, nil, MethodNotFoundError
	}

	var method = value.MethodByName(fn)
	if !method.IsValid() {
		return nil, nil, MethodNotFoundError
	}

	var typ = method.Type().In(1).Elem()
	var argv = reflect.New(typ).Interface()

	if err := proto.Unmarshal(req, argv.(proto.Message)); err != nil {
		return nil, argv, err
	}

	var params = []reflect.Value{reflect.ValueOf(ctx), reflect.ValueOf(argv)}
	var result = method.Call(params)

	if result[1].Interface() == nil {
		return result[0].Interface().(proto.Message), argv, nil
	} else {
		return nil, argv, result[1].Interface().(error)
	}
}

func Register(structPtr interface{}) {

	var typ = reflect.TypeOf(structPtr)
	var kind = typ.Kind()
	if kind != reflect.Ptr {
		panic(errors.New("not ptr, is " + kind.String()))
	}

	var value = reflect.ValueOf(structPtr)
	var name = reflect.Indirect(value).Type().Name()
	if name == "" {
		panic(errors.New("rpc.Register: no service name for type " + typ.String()))
	}

	name = strings.Replace(name, "Service", "", -1)

	m[name] = value
}
